#ifndef __CORE_MATH__
#define __CORE_MATH__

#include <assert.h>
#include <math.h>

#ifdef INFINITY
#undef INFINITY
#endif

#ifdef FLT_EPSILON
#undef FLT_EPSILON
#endif

#define DEG2RAD(a)				((a) * rtMath::M_DEG2RAD)
#define RAD2DEG(a)				((a) * rtMath::M_RAD2DEG)

#define SEC2MS(t)				(rtMath::FtoiFast((t) * rtMath::M_SEC2MS))
#define MS2SEC(t)				((t) * rtMath::M_MS2SEC)

#define	ANGLE2SHORT(x)			(rtMath::FtoiFast((x) * 65536.0f / 360.0f) & 65535)
#define	SHORT2ANGLE(x)			((x) * (360.0f / 65536.0f))

#define	ANGLE2BYTE(x)			(rtMath::FtoiFast((x) * 256.0f / 360.0f) & 255)
#define	BYTE2ANGLE(x)			((x) * (360.0f / 256.0f))

#define FLOATSIGNBITSET(f)		((*(const unsigned long *)&(f)) >> 31)
#define FLOATSIGNBITNOTSET(f)	((~(*(const unsigned long *)&(f))) >> 31)
#define FLOATNOTZERO(f)			((*(const unsigned long *)&(f)) & ~(1<<31))
#define INTSIGNBITSET(i)		(((const unsigned long)(i)) >> 31)
#define INTSIGNBITNOTSET(i)		((~((const unsigned long)(i))) >> 31)

#define	FLOAT_IS_NAN(x)			(((*(const unsigned long *)&x) & 0x7f800000) == 0x7f800000)
#define FLOAT_IS_INF(x)			(((*(const unsigned long *)&x) & 0x7fffffff) == 0x7f800000)
#define FLOAT_IS_IND(x)			((*(const unsigned long *)&x) == 0xffc00000)
#define	FLOAT_IS_DENORMAL(x)	(((*(const unsigned long *)&x) & 0x7f800000) == 0x00000000 && \
	((*(const unsigned long *)&x) & 0x007fffff) != 0x00000000)

#define IEEE_FLT_MANTISSA_BITS	23
#define IEEE_FLT_EXPONENT_BITS	8
#define IEEE_FLT_EXPONENT_BIAS	127
#define IEEE_FLT_SIGN_BIT		31

#define IEEE_DBL_MANTISSA_BITS	52
#define IEEE_DBL_EXPONENT_BITS	11
#define IEEE_DBL_EXPONENT_BIAS	1023
#define IEEE_DBL_SIGN_BIT		63

#define IEEE_DBLE_MANTISSA_BITS	63
#define IEEE_DBLE_EXPONENT_BITS	15
#define IEEE_DBLE_EXPONENT_BIAS	0
#define IEEE_DBLE_SIGN_BIT		79

namespace Core
{
	template<class T> inline int	MaxIndex(T x, T y) { return  (x > y) ? 0 : 1; }
	template<class T> inline int	MinIndex(T x, T y) { return (x < y) ? 0 : 1; }

	template<class T> inline T	    Max3(T x, T y, T z) { return (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z); }
	template<class T> inline T	    Min3(T x, T y, T z) { return (x < y) ? ((x < z) ? x : z) : ((y < z) ? y : z); }
	template<class T> inline int	Max3Index(T x, T y, T z) { return (x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2); }
	template<class T> inline int	Min3Index(T x, T y, T z) { return (x < y) ? ((x < z) ? 0 : 2) : ((y < z) ? 1 : 2); }

	template<class T> inline T	    Sign(T f) { return (f > 0) ? 1 : ((f < 0) ? -1 : 0); }
	template<class T> inline T	    Square(T x) { return x * x; }
	template<class T> inline T	    Cube(T x) { return x * x * x; }

	class rtVector3;

	class rtMath 
	{
	public:

		static void					Init(void);

		static float				RSqrt(float x);

		static float				InvSqrt(float x);
		static float				InvSqrt16(float x);
		static double				InvSqrt64(float x);

		static float				Sqrt(float x);
		static float				Sqrt16(float x);
		static double				Sqrt64(float x);

		static float				Sin(float a);
		static float				Sin16(float a);
		static double				Sin64(float a);

		static float				Cos(float a);
		static float				Cos16(float a);
		static double				Cos64(float a);

		static void					SinCos(float a, float& s, float&c);
		static void					SinCos16(float a, float& s, float& c);
		static void					SinCos64(float a, double& s, double& c);

		static float				Tan(float a);
		static float				Tan16(float a);
		static double				Tan64(float a);

		static float				ASin(float a);			
		static float				ASin16(float a);
		static double				ASin64(float a);

		static float				ACos(float a);
		static float				ACos16(float a);
		static double				ACos64(float a);

		static float				ATan(float a);
		static float				ATan16(float a);
		static double				ATan64(float a);

		static float				ATan(float y, float x);
		static float				ATan16(float y, float x);
		static double				ATan64(float y, float x);

		static float				Pow(float x, float y);
		static float				Pow16(float x, float y);
		static double				Pow64(float x, float y);

		static float				Exp(float f);
		static float				Exp16(float f);
		static double				Exp64(float f);

		static float				Log(float f);
		static float				Log16(float f);
		static double				Log64(float f);

		static int					IPow(int x, int y);
		static int					ILog2(float f);
		static int					ILog2(int i);

		static int					BitsForFloat(float f);
		static int					BitsForInteger(int i);
		static int					MaskForFloatSign(float f);
		static int					MaskForIntegerSign(int i);
		static int					FloorPowerOfTwo(int x);
		static int					CeilPowerOfTwo(int x);
		static bool					IsPowerOfTwo(int x);
		static int					BitCount(int x);
		static int					BitReverse(int x);

		static int					Abs(int x);
		static float				Fabs(float f);
		static float				Floor(float f);
		static float				Ceil(float f);
		static float				Rint(float f);
		static int					Ftoi(float f);
		static int					FtoiFast(float f);
		static unsigned long		Ftol(float f);
		static unsigned long		FtolFast(float);

		static signed char			ClampChar(int i);
		static signed short			ClampShort(int i);
		static int					ClampInt(int min, int max, int value);
		static float				ClampFloat(float min, float max, float value);

		static float				AngleNormalize360(float angle);
		static float				AngleNormalize180(float angle);
		static float				AngleDelta(float angle1, float angle2);

		static int					FloatToBits(float f, int exponentBits, int mantissaBits);
		static float				BitsToFloat(int i, int exponentBits, int mantissaBits);

		static int					FloatHash(const float* array, const int numFloats);

		static void                 MinMax(float& min, float& max, const float* src, const int count);
		static void                 MinMax(rtVector3& min, rtVector3& max, const rtVector3* src, const int count);

		static const float			PI;
		static const float			TWO_PI;
		static const float			HALF_PI;
		static const float			ONEFOURTH_PI;
		static const float			E;
		static const float			SQRT_TWO;
		static const float			SQRT_THREE;
		static const float			SQRT_1OVER2;
		static const float			SQRT_1OVER3;
		static const float			M_DEG2RAD;
		static const float			M_RAD2DEG;
		static const float			M_SEC2MS;
		static const float			M_MS2SEC;
		static const float			INFINITY;
		static const float			FLT_EPSILON;

	private:
		enum 
		{
			LOOKUP_BITS				= 8,							
			EXP_POS					= 23,							
			EXP_BIAS				= 127,							
			LOOKUP_POS				= (EXP_POS-LOOKUP_BITS),
			SEED_POS				= (EXP_POS-8),
			SQRT_TABLE_SIZE			= (2<<LOOKUP_BITS),
			LOOKUP_MASK				= (SQRT_TABLE_SIZE-1)
		};

		union _flint 
		{
			unsigned int			i;
			float					f;
		};

		static unsigned int			iSqrt[SQRT_TABLE_SIZE];
		static bool					initialized;
	};
}
#endif


